# # 代码2-1
# # 使用torchtext.utils.download_from_url类下载数据文件
# import torchtext
#
# url = 'https://mirrors.cloud.tencent.com/pypi/packages/51/59/d190ffe6fac2f5c7a301bd9ffd5f69b9a1925e08dbfecd6ee1e8b816fedb/torchtext-0.9.0-cp38-cp38-win_amd64.whl'
# torchtext.utils.download_from_url(url, 'data/torchtext.whl')
#
#
#
# 代码2-2
# 导入unicode_csv_reader函数，用于读取CSV文件并处理Unicode字符
# from torchtext.utils import unicode_csv_reader
#
# # 导入io模块，用于处理文件输入输出操作
# import io
#
# # 定义数据文件的路径
# # data_path = 'data/shaonaiyi.txt'
# data_path = 'data/shaonaiyi.csv'
#
# # 使用io.open打开指定路径的文件，编码方式为utf8
# with io.open(data_path, encoding='utf8') as f:
#     # 使用unicode_csv_reader读取打开的文件对象f
#     reader = unicode_csv_reader(f)
#
#     # 遍历reader中的每一行数据（假设每一行是一个列表）
#     for row in reader:
#         # 打印当前行的数据
#         print(row)
#
# # 代码2-3
# # 使用torchtext.utils.extract_archive类读取数据文件
# import torchtext
# from_path = 'data/test_data.zip'
# to_path = 'data/'
# url = 'https://mirrors.cloud.tencent.com/pypi/packages/51/59/d190ffe6fac2f5c7a301bd9ffd5f69b9a1925e08dbfecd6ee1e8b816fedb/torchtext-0.9.0-cp38-cp38-win_amd64.whl'
# # 从指定的URL下载文件，并将其保存到本地的from_path路径中
# torchtext.utils.download_from_url(url, from_path)
# # 将本地from_path路径中的压缩文件解压到to_path路径中
# torchtext.utils.extract_archive(from_path, to_path)
#
#
#
# # 代码2-4
# # 使用torchvision.transforms.Compose类组合图像多种变换处理
# from torchvision import transforms
#
# transforms.Compose([transforms.CenterCrop(10), transforms.ToTensor()])

# from torchvision import transforms
# from PIL import Image
# import matplotlib.pyplot as plt
#
# # 定义图像路径
# image_path = 'data/公众号二维码.jpg'  # 请替换为你的图像路径
#
# # 加载图像
# image = Image.open(image_path)
#
# # 定义变换
# transform = transforms.Compose([
#     transforms.CenterCrop(100),  # 中心裁剪到100x100像素
#     transforms.ToTensor()       # 转换为张量
# ])
#
# # 应用变换
# transformed_image = transform(image)
#
# # 打印输出结果
# print("Transformed image shape:", transformed_image.shape)
# print("Transformed image data type:", transformed_image.dtype)
#
# # 可视化原始图像和变换后的图像
# fig, ax = plt.subplots(1, 2)  # 创建一个包含两个子图的图形对象
# ax[0].imshow(image)  # 在第一个子图中显示原始图像
# ax[0].set_title('Original Image')  # 设置第一个子图的标题为 "Original Image"
# ax[0].axis('off')  # 关闭第一个子图的坐标轴
#
# # 将张量转换回PIL图像以进行可视化
# transformed_image_pil = transforms.ToPILImage()(transformed_image)
# ax[1].imshow(transformed_image_pil)  # 在第二个子图中显示变换后的图像
# ax[1].set_title('Transformed Image')  # 设置第二个子图的标题为 "Transformed Image"
# ax[1].axis('off')  # 关闭第二个子图的坐标轴
#
# plt.show()  # 显示图形窗口
#
#
#
#
# # 代码2-5
# from torchvision.transforms import FiveCrop
# # 使用torchvision.transforms.FiveCrop类裁剪图像
# transform = transforms.Compose([
#         FiveCrop(size),  # size是读取的PIL图像
#         Lambda(lambda crops: torch.stack([ToTensor()(crop) for crop in crops]))  # 返回4维张量
#         ])
# input, target = batch  # input是维张量, target是2维张量
# bs, ncrops, c, h, w = input.size()
# result = model(input.view(-1, c, h, w))  # 改变数据维度
# result_avg = result.view(bs, ncrops, -1).mean(1)  # 求均值
#
#
# import torch
# from torchvision import transforms
# from PIL import Image
# import matplotlib.pyplot as plt
#
# # 定义图像路径
# image_path = 'data/公众号二维码.jpg'  # 请替换为你的图像路径
#
# # 加载图像
# image = Image.open(image_path)
#
# # 定义 FiveCrop 变换
# size = 150  # 设置裁剪大小
# five_crop_transform = transforms.Compose([
#     transforms.FiveCrop(size),  # 中心裁剪到 size x size 像素
#     transforms.Lambda(lambda crops: torch.stack([transforms.ToTensor()(crop) for crop in crops]))  # 返回4维张量
# ])
#
# # 应用变换
# transformed_image = five_crop_transform(image)
#
# # 打印输出结果
# print("Transformed image shape:", transformed_image.shape)  # 打印变换后图像的形状
# print("Transformed image data type:", transformed_image.dtype)  # 打印变换后图像的数据类型
#
# # 可视化原始图像和变换后的图像
# fig, ax = plt.subplots(1, 6)
# ax[0].imshow(image)
# ax[0].set_title('Original Image')
# ax[0].axis('off')
#
# for i in range(5):
#     ax[i + 1].imshow(transforms.ToPILImage()(transformed_image[i]))
#     ax[i + 1].set_title(f'Crop {i + 1}')
#     ax[i + 1].axis('off')
#
# plt.show()
#
#
# # 假设你有一个预训练的模型
# class SimpleModel(torch.nn.Module):
#     def __init__(self):
#         super(SimpleModel, self).__init__()
#         # 定义一个2维卷积层，输入通道数为3（RGB图像），输出通道数为1，卷积核大小为3x3，步幅为1，填充为1
#         self.conv = torch.nn.Conv2d(3, 1, kernel_size=3, stride=1, padding=1)
#         # 定义一个全连接层，输入特征数为1 * size * size，输出特征数为10
#         self.fc = torch.nn.Linear(1 * size * size, 10)
#
#     def forward(self, x):
#         # 通过卷积层进行前向传播
#         x = self.conv(x)
#         # 将卷积层的输出展平成一维向量
#         x = x.view(x.size(0), -1)
#         # 通过全连接层进行前向传播
#         x = self.fc(x)
#         return x
#
#
# model = SimpleModel()
#
# # 模拟一个批次数据
# batch = (transformed_image.unsqueeze(0), torch.tensor([1]))  # input是4维张量, target是2维张量
# input, target = batch  # input是4维张量, target是2维张量
# bs, ncrops, c, h, w = input.size()
# result = model(input.view(-1, c, h, w))  # 改变数据维度
# result_avg = result.view(bs, ncrops, -1).mean(1)  # 求均值
#
# # 打印最终结果
# print("Result average shape:", result_avg.shape)  # 打印结果的平均形状
# print("Result average:", result_avg)  # 打印结果的平均值
#
#
# # 代码2-6
# # 使用句子计数器
# from torchtext.data.functional import sentencepiece_numericalizer
#
# # 创建一个sentencepiece_numericalizer实例，用于将句子转换为数值ID
# sp_id_generator = sentencepiece_numericalizer(sp_model)
#
# # 定义一个包含多个句子的列表
# list_a = ['sentencepiece encode as pieces', 'examples to try!']
#
# # 使用sp_id_generator将每个句子转换为数值ID序列
# result = list(sp_id_generator(list_a))
#
# # 打印结果
# print(result)
#
# # 注意事项:
# # 1. 确保你已经加载了一个有效的SentencePiece模型文件（例如'sp_model.model'）。
# # 2. 输出的结果是一个嵌套列表，其中每个子列表对应输入列表中的一个句子，并包含该句子的数值ID序列。
#
#
# # 代码2-7
# # 使用句子分词器对文本句子成分进行标记
# from torchtext.data.functional import sentencepiece_tokenizer
#
# # 创建一个sentencepiece_tokenizer实例，用于将句子分解为词汇单元
# sp_tokens_generator = sentencepiece_tokenizer(sp_model)
#
# # 定义一个包含多个句子的列表
# list_a = ['sentencepiece encode as pieces', 'examples to try!']
#
# # 使用sp_tokens_generator将每个句子分解为词汇单元
# result = list(sp_tokens_generator(list_a))
#
# # 打印结果
# print(result)
#
# # 注意事项:
# # 1. 确保你已经加载了一个有效的SentencePiece模型文件（例如'sp_model.model'）。
# # 2. 输出的结果是一个嵌套列表，其中每个子列表对应输入列表中的一个句子，并包含该句子的词汇单元。
#
#
# # 代码2-8
# # 使用句子分词器对文本句子做分词处理
# from torchtext.data.functional import simple_space_split
#
# # 定义一个包含多个句子的列表
# list_a = ['Sentencepiece encode as pieces', 'example to try!']
#
# # 使用simple_space_split将每个句子按空格分割为单词列表
# result = list(simple_space_split(list_a))
#
# # 打印结果
# print(result)
#
# # 注意事项:
# # 1. simple_space_split是一个简单的基于空格的分词方法，适用于已经预处理过的句子。
# # 2. 输出的结果是一个嵌套列表，其中每个子列表对应输入列表中的一个句子，并包含该句子的单词列表。
#
#
#
#


# # 代码2-11
# # 判断猫狗
# def find_label(str):
#     first, last = 0, 0
#     # 从字符串 str 的最后一个字符开始，逐个向前遍历到第一个字符range(start, stop, step)，step：每次循环索引减1
#     for i in range(len(str) - 1, -1, -1):
#         # 确定所需要截取的结束索引
#         if str[i] == '%' and str[i - 1] == '.':
#             last = i - 1
#         # 确定所需要截取的开始索引
#         if (str[i] == 'c' or str[i] == 'd') and str[i - 1] == '/':
#             first = i
#             break
#
#     # 提取子字符串，截取出dog或者cat
#     name = str[first: last]
#     if name == 'dog':
#         return 1
#     else:
#         return 0
#
# # 代码2-9
# # 包含路径与标签的列表
# # 得到一个包含路径与标签的列表
# import torch
# from torchvision import transforms
# from torch.utils.data import Dataset, DataLoader
# from PIL import Image
# import torch.nn.functional as F
#
# def init_process(path, lens):
#     data = []
#     name = find_label(path)
#     for i in range(lens[0], lens[1]):
#         # path % i 会将 path 中的 %d 替换为 i 的值，生成一个新的列表 [formatted_path, name]
#         data.append([path % i, name])
#
#     return data
#
#
# # 代码2-10
# # 参数传入
# path1 = 'data/training_data/cats/cat.%d.jpg'
# data1 = init_process(path1, [0, 500])
#
#
# # 代码2-12
# # 经过函数处理后的四个列表
# path1 = 'data/training_data/cats/cat.%d.jpg'
# data1 = init_process(path1, [0, 500])
# path2 = 'data/training_data/dogs/dog.%d.jpg'
# data2 = init_process(path2, [0, 500])
# path3 = 'data/testing_data/cats/cat.%d.jpg'
# data3 = init_process(path3, [1000, 1200])
# path4 = 'data/testing_data/dogs/dog.%d.jpg'
# data4 = init_process(path4, [1000, 1200])
# print("data1:", data1)
# print("data2:", data2)
# print("data3:", data3)
# print("data4:", data4)
#

#
#
# # 代码2-11
# # 判断猫狗
# def find_label(str):
#     first, last = 0, 0
#     for i in range(len(str) - 1, -1, -1):
#         if str[i] == '%' and str[i - 1] == '.':
#             last = i - 1
#         if (str[i] == 'c' or str[i] == 'd') and str[i - 1] == '/':
#             first = i
#             break
#
#     name = str[first: last]
#     if name == 'dog':
#         return 1
#     else:
#         return 0
#
# # 代码2-9
# # 包含路径与标签的列表
# # 得到一个包含路径与标签的列表
# import torch
# from torchvision import transforms
# from torch.utils.data import Dataset, DataLoader
# from PIL import Image
# import torch.nn.functional as F
#
# def init_process(path, lens):
#     data = []
#     name = find_label(path)
#     for i in range(lens[0], lens[1]):
#         data.append([path % i, name])
#
#     return data
#
#
# # 代码2-10
# # 参数传入
# path1 = 'data/training_data/cats/cat.%d.jpg'
# data1 = init_process(path1, [0, 500])
#
#
# # 代码2-12
# # 经过函数处理后的四个列表
# path1 = 'data/training_data/cats/cat.%d.jpg'
# data1 = init_process(path1, [0, 500])
# path2 = 'data/training_data/dogs/dog.%d.jpg'
# data2 = init_process(path2, [0, 500])
# path3 = 'data/testing_data/cats/cat.%d.jpg'
# data3 = init_process(path3, [1000, 1200])
# path4 = 'data/testing_data/dogs/dog.%d.jpg'
# data4 = init_process(path4, [1000, 1200])
# print("data1:", data1)
# print("data2:", data2)
# print("data3:", data3)
# print("data4:", data4)
#
#

## 代码2-13
## 利用Image处理图片
# 定义一个加载图片的函数，将图片转换为RGB格式
# def Myloader(path):
#     return Image.open(path).convert('RGB')
#
#
#
## 代码2-14
## 重写Dataset类
# class MyDataset(Dataset):
#     # 初始化函数，接收数据、转换操作和加载器作为参数
#     def __init__(self, data, transform, loder):
#         self.data = data  # 保存传入的数据
#         self.transform = transform  # 保存传入的转换操作
#         self.loader = loder  # 保存传入的加载器
#
#     # 根据索引获取数据项，并对这些数据进行转换操作
#     def __getitem__(self, item):
#         img, label = self.data[item]  # 从数据集中获取图像和标签
#         img = self.loader(img)  # 使用加载器加载图像
#         img = self.transform(img)  # 对图像进行转换操作
#         return img, label  # 返回处理后的图像和标签
#
#     # 返回数据集的长度
#     def __len__(self):
#         return len(self.data)  # 返回数据集中数据项的数量
#
#
#
## 代码2-15
## transform
# 定义一系列的图像转换操作，包括随机水平翻转、随机垂直翻转、调整大小、转换为张量和归一化
# transform = transforms.Compose([
#           transforms.RandomHorizontalFlip(p=0.3),  # 以0.3的概率进行随机水平翻转
#           transforms.RandomVerticalFlip(p=0.3),  # 以0.3的概率进行随机垂直翻转
#           transforms.Resize((256, 256)),  # 将图像调整为256x256的大小
#           transforms.ToTensor(),  # 将图像转换为张量
#           transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))])  # 对图像进行归一化处理
#
#
#
#
#
# 代码2-16
# 猫狗分类的数据预处理
import torch
from torchvision import transforms
from torch.utils.data import Dataset, DataLoader
from PIL import Image
import torch.nn.functional as F

def Myloader(path):
    return Image.open(path).convert('RGB')


# 得到一个包含路径与标签的列表
def init_process(path, lens):
    data = []
    name = find_label(path)
    for i in range(lens[0], lens[1]):
        data.append([path % i, name])
    return data


class MyDataset(Dataset):

    def __init__(self, data, transform, loder):
        self.data = data
        self.transform = transform
        self.loader = loder

    def __getitem__(self, item):
        img, label = self.data[item]
        img = self.loader(img)
        img = self.transform(img)
        return img, label

    def __len__(self):
        return len(self.data)


def find_label(str):
    first, last = 0, 0
    for i in range(len(str) - 1, -1, -1):
        if str[i] == '%' and str[i - 1] == '.':
            last = i - 1
        if (str[i] == 'c' or str[i] == 'd') and str[i - 1] == '/':
            first = i
            break

    name = str[first:last]
    if name == 'dog':
        return 1
    else:
        return 0


def load_data():
    transform = transforms.Compose([
              transforms.RandomHorizontalFlip(p=0.3),
              transforms.RandomVerticalFlip(p=0.3),
              transforms.Resize((256, 256)),
              transforms.ToTensor(),
              transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))])
    path1 = 'data/training_data/cats/cat.%d.jpg'
    data1 = init_process(path1, [0, 500])
    path2 = 'data/training_data/dogs/dog.%d.jpg'
    data2 = init_process(path2, [0, 500])
    path3 = 'data/testing_data/cats/cat.%d.jpg'
    data3 = init_process(path3, [1000, 1200])
    path4 = 'data/testing_data/dogs/dog.%d.jpg'
    data4 = init_process(path4, [1000, 1200])
    # 1300个训练
    train_data = data1 + data2 + data3[0: 150] + data4[0: 150]

    train = MyDataset(train_data, transform=transform, loder=Myloader)
    # 100个测试
    test_data = data3[150: 200] + data4[150: 200]
    test = MyDataset(test_data, transform=transform, loder=Myloader)

    train_data = DataLoader(dataset=train, batch_size=10, shuffle=True, num_workers=0)
    test_data = DataLoader(dataset=test, batch_size=1, shuffle=True, num_workers=0)

    return train_data, test_data

if __name__ == '__main__':
    # 加载数据
    train_data, test_data = load_data()
    # 打印一些训练数据样本
    print("Training Data Samples:")
    for i, (images, labels) in enumerate(train_data):
        print(f"Batch {i + 1}:")
        print("Images shape:", images.shape)
        print("Labels:", labels)
        if i >= 2:  # 只打印前3个批次的数据
            break

    # 打印一些测试数据样本
    print("\nTesting Data Samples:")
    for i, (images, labels) in enumerate(test_data):
        print(f"Batch {i + 1}:")
        print("Images shape:", images.shape)
        print("Labels:", labels)
        if i >= 2:  # 只打印前3个批次的数据
            break